//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#include "DisplayObject.h"
#include "DisplayObjectContainer.h"

namespace D2 {

DisplayObject::DisplayObject() {
	_parent = 0;
	name = "";

	_alpha = 0;
	_x = 0;
	_y = 0;
	_rotation = 0;
	_scaleX = 1;
	_scaleY = 1;
	
	_regX = 0;
	_regY = 0;

	_width = 0;
    _height = 0;

	_clip = 0;

	mouseEnabled = true;
	visible = true;

	_autoForegroundEnabled = false;

	_data = 0;
	autoDestroyUserData = false;
}

DisplayObject::DisplayObject(int x, int y, int width, int height) {
	_parent = 0;
	name = "";

	_alpha = 0;
	_x = x;
	_y = y;
	_rotation = 0;
	_scaleX = 1;
	_scaleY = 1;
	
	_regX = 0;
	_regY = 0;

	_width = width;
    _height = height;

	_clip = 0;

	mouseEnabled = true;
	visible = true;

	_autoForegroundEnabled = false;

	_data = 0;
	autoDestroyUserData = false;
}

DisplayObject::~DisplayObject(void) {
	Unattach();

	if(autoDestroyUserData && _data != 0) {

	}
}

DisplayObjectContainer* DisplayObject::GetParent() {
	return _parent;
}

DisplayObjectContainer* DisplayObject::GetRoot() {
	DisplayObjectContainer* parent = this->_parent;
	while(parent != 0) {
		if(parent->_parent == 0) {
			return parent;
		}
		parent = parent->_parent;
	}

	return 0;
}

bool DisplayObject::isVisible() {
	return visible;
}

bool DisplayObject::_hittest_boundingBox(int x, int y) {     
	D2D1_POINT_2F point = globalToLocal(x, y);

    return (point.x > 0 && point.x < _width && point.y > 0 && point.y < _height);
}

bool DisplayObject::_hittest_pixel(Direct2DContext* ctx) {
	bool hit = false;
	//try {
		//hit = ctx->getImageData(0, 0, 1, 1).data[3] > 1;
	//} catch (e) {
		//if (!DisplayObject.suppressCrossDomainErrors) {
		//	throw "An error has occured. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
		//}
	//}
 
	return hit;
}

bool DisplayObject::_hittest_pixel(int x, int y) {
	//var ctx = DisplayObject._hitTestContext;
	//var canvas = DisplayObject._hitTestCanvas;
	bool hit = false;
	/*
	ctx->setTransform(1,  0, 0, 1, -x, -y);
	this->Draw(ctx);

	bool hit = _hittest_pixel(ctx);
            
	//canvas.width = 0;
	//canvas.width = 1;
    */       
	return hit;
}

D2D1::Matrix3x2F DisplayObject::getMatrix() {
	const D2D1::Matrix3x2F rotation = D2D1::Matrix3x2F::Rotation(_rotation);
	const D2D1::Matrix3x2F translation = D2D1::Matrix3x2F::Translation(_x, _y);
	const D2D1::Matrix3x2F scalation = D2D1::Matrix3x2F::Scale(_scaleX, _scaleY); //, D2D1::Point2F(child->_regX, child->_regY));
	const D2D1::Matrix3x2F pivot = D2D1::Matrix3x2F::Translation(_regX, _regY);

	D2D1::Matrix3x2F local;
	local = rotation * pivot * scalation * translation;

	return local;
}

/*
* ConcatenatedMatrix
* returns the combined matrix with the parent. (parent matrix chaining)
*/
D2D1::Matrix3x2F DisplayObject::getConcatenatedMatrix() {
	if(_parent != 0) {
		return getMatrix() * _parent->getConcatenatedMatrix();
	}
	return getMatrix();
}

D2D1_POINT_2F DisplayObject::globalToLocal(float x, float y) {
	D2D1_POINT_2F point;
	point.x = (float)x;
	point.y = (float)y;

	D2D1::Matrix3x2F mtx = getConcatenatedMatrix();
	mtx.Invert();
	D2D1_POINT_2F transformed_point = mtx.TransformPoint(point);

	return transformed_point;
}

/*
* Transforms the specified x and y position from the coordinate space of this display object to the
* coordinate space of the target display object. Returns a Point instance with x and y properties
* correlating to the transformed position in the target's coordinate space. Effectively the same as calling
* var pt = this.localToGlobal(x, y); pt = target.globalToLocal(pt.x, pt.y);
*/
D2D1_POINT_2F DisplayObject::localToGlobal(float x, float y) {
	D2D1_POINT_2F point;
	point.x = (float)x;
	point.y = (float)y;

	D2D1::Matrix3x2F mtx = getConcatenatedMatrix();
	D2D1_POINT_2F transformed_point = mtx.TransformPoint(point);

	return transformed_point;
}

/*
* Transforms the specified x and y position from the coordinate space of this display object to the
* coordinate space of the target display object. Returns a Point instance with x and y properties
* correlating to the transformed position in the target's coordinate space. Effectively the same as calling
* var pt = this.localToGlobal(x, y); pt = target.globalToLocal(pt.x, pt.y);
*/
D2D1_POINT_2F DisplayObject::localToLocal(float x, float y, DisplayObject* target) {
	D2D1_POINT_2F point = this->localToGlobal(x, y);
	return target->globalToLocal(point.x, point.y);
}

/*
* removes and deletes the displayObject from the DisplayList.
*/
void DisplayObject::Destroy() {
	Unattach(true);
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////
	
/*
* attaches this to a container.
*/
void DisplayObject::Attach(DisplayObjectContainer* displayObjectContainer) {
	displayObjectContainer->addChild(this);
}

/*
* removes this from his parent container.
* bool del (default) -> deletes object out of the memory.
*/
void DisplayObject::Unattach(bool del) {
	if(_parent != 0) {
		_parent->removeChild(this, del);
	}
}

/*
* gives this the keyboard input focus.
*/
void DisplayObject::focus() {
	DisplayObjectContainer* parent = this->_parent;
	while(parent != 0) {
		if(parent->_parent == 0) {
			parent->focus(this);
		}
		parent = parent->_parent;
	}
}

/*
* removes from this the keyboard input focus.
*/
void DisplayObject::unfocus() {
	DisplayObjectContainer* parent = this->_parent;
	while(parent != 0) {
		if(parent->_parent == 0) {
			parent->unfocus();
		}
		parent = parent->_parent;
	}
}

/*
* pushs this to the foreground of his parent container.
*/
void DisplayObject::toForeground() {
	if(_parent != 0) { // && target->_parent instanceof Container) {
		_parent->toForeground(this); //_parent->addChild(this); //same as container.addChildAt(target, container.getNumChildren()-1);	
	}
}

///

void DisplayObject::beginClip(Direct2DContext* ctx) {
	if(_clip != 0) {
		ctx->save();
		_clip->Execute(ctx);
		ctx->clip();
	}
}

void DisplayObject::endClip(Direct2DContext* ctx) {
	if(_clip != 0) {
		ctx->restore();
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
//                                        USER DATA                                          //
///////////////////////////////////////////////////////////////////////////////////////////////

void DisplayObject::SetUserData(void* data) {
	_data = data;
}

void* DisplayObject::GetUserData() {
	return _data;
}

}